package org.sun.dbrd;

import java.io.BufferedReader;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.IOException;
import java.io.PrintStream;
import java.io.PrintWriter;
import java.io.StringReader;
import java.io.StringWriter;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Properties;

import org.dbrd.detector.tfidf.TFCollection;
import org.dbrd.model.DetectionBugReport;
import org.dbrd.model.DuplicateDetectionBugReport;
import org.dbrd.model.ISummaryFileFormatConstants;
import org.dbrd.model.MasterDetectionBugReport;
import org.dbrd.util.PropertiesReader;
import org.scnb.es.RestClientUtils;

/**
 * to identify each term, (unigram and bigram).
 * 
 * @author workshop
 * 
 */
public abstract class IdsPreprocessor {
	RestClientUtils restClientUtils = new RestClientUtils();

	public static class VersionOrderMap {

		private int lastVersion;

		private HashMap<String, Integer> versionOrderMap;

		private HashSet<String> unhandledVersions;

		public VersionOrderMap(File versionOrderFile) {
			this.lastVersion = Integer.MIN_VALUE;
			versionOrderMap = new HashMap<String, Integer>();
			this.unhandledVersions = new HashSet<String>();
			this.initialize(versionOrderFile);
		}

		protected void initialize(File versionOrderFile) {
			try {
				BufferedReader reader = new BufferedReader(new FileReader(versionOrderFile));
				for (String line = reader.readLine(); line != null; line = reader.readLine()) {
					String[] sections = line.split("\\|\\+\\+\\|");
					String key = sections[1];
					int value = Integer.parseInt(sections[0]);
					versionOrderMap.put(key, value);
				}
				reader.close();
			} catch (IOException e) {
				throw new RuntimeException(e);
			}

			this.unhandledVersions.add("1.0.0");
			this.unhandledVersions.add("1.0.1");
			this.unhandledVersions.add("1.0.2");
			this.unhandledVersions.add("1.0.3");
			this.unhandledVersions.add("208");
			this.unhandledVersions.add("4.5.2");
			this.unhandledVersions.add("605");
			this.unhandledVersions.add("613");
			this.unhandledVersions.add("614");
			this.unhandledVersions.add("619");
			this.unhandledVersions.add("632");
			this.unhandledVersions.add("644m11");
			this.unhandledVersions.add("680");
			this.unhandledVersions.add("mytestversion");
			this.unhandledVersions.add("myversion");
			this.unhandledVersions.add("nb-plugin 1.2.0");
			this.unhandledVersions.add("rpt 0.5.0.20070627");
			this.unhandledVersions.add("unspecified");
			this.unhandledVersions.add("v1.1");
			this.unhandledVersions.add("version58");
			this.unhandledVersions.add("current");
		}

		private static String preprocessVersion(String version) {
			version = version.trim().toLowerCase();
			// version = version.replaceFirst("\\.0\\s", " ");
			version = version.replace(".0rc", " rc");
			if (version.contains("ooo ")) {
				version = version.replace(".0.0", "");
				version = version.replace(".0 ", " ");
				version = version.replaceAll("\\.0$", "");
			}

			version = version.replace("_m", "m");
			version = version.replace(" m", "m");
			version = version.replace("openoffice.org", "ooo");
			int rcIndex = version.indexOf("rc");
			if (rcIndex > 0) {
				char cBeforeRC = version.toCharArray()[rcIndex - 1];
				if (cBeforeRC != ' ') {
					version = version.replace("rc", " rc");
				}
			}
			return version;
		}

		// private HashSet<String> messages = new HashSet<String>();

		public int getVersionOrder(String version, int reportId) {
			String preprocessedVersion = preprocessVersion(version);
			if (this.unhandledVersions.contains(preprocessedVersion)) {
				if (this.lastVersion == Integer.MIN_VALUE) {
					throw new RuntimeException();
				}
				return this.lastVersion;
			} else {
				Integer order = this.versionOrderMap.get(preprocessedVersion);
				if (order == null) {
					// String message = "unhandled version [" + version + ", " +
					// preprocessedVersion + "] in report " + reportId;
					// if (!messages.contains(version)) {
					// messages.add(version);
					// System.err.println(message);
					// }
					// return 0;
					throw new RuntimeException("unhandled version [" + version + ", " + preprocessedVersion + "] in report "
							+ reportId);
				} else {
					this.lastVersion = order.intValue();
					return order.intValue();
				}
			}
		}
	}

	private static class Pair {
		public Integer termId;
		public int termFrequency;

		@Override
		public String toString() {
			return termId + ":" + termFrequency;
		}

		public Pair(Integer termId, int termFrequency) {
			super();
			this.termId = termId;
			this.termFrequency = termFrequency;
		}

	}

	private VersionOrderMap versionOrderMap;

	private int sequenceId = 0;

	private final Map<String, Integer> idMap;

	private final File summaryPath;			// reports.txt

	private final File resultPath;			// reports-large.txt

	private final File mappingPath;			// reports-mapping.txt

	public IdsPreprocessor(File summaryPath, File resultPath, File mappingPath, File versionOrderFile) {
		this.idMap = new HashMap<String, Integer>();
		this.summaryPath = summaryPath;
		this.resultPath = resultPath;
		this.mappingPath = mappingPath;
		this.versionOrderMap = this.createVersionOrderMap(versionOrderFile);
		this.identify();
		this.outputMapping();
	}

	protected abstract VersionOrderMap createVersionOrderMap(File versionOrderFile);

	private void outputMapping() {
		PrintStream out;
		try {
			out = new PrintStream(this.mappingPath);
			for (String key : this.idMap.keySet()) {
				out.println(this.idMap.get(key) + " = " + key);
			}
			out.close();
		} catch (FileNotFoundException e) {
			e.printStackTrace();
		}

	}

	private Integer getIdentifier(String term) {
		Integer id = this.idMap.get(term);
		if (id == null) {
			id = new Integer(this.sequenceId++);
			this.idMap.put(term, id);
		}
		return id;
	}

	private ArrayList<Pair> identify(TFCollection tfCollection) {
		ArrayList<Pair> result = new ArrayList<Pair>();
		String[] terms = tfCollection.getTerms();
		for (String term : terms) {
			result.add(new Pair(this.getIdentifier(term), tfCollection.getFrequency(term)));
		}
		Collections.sort(result, new Comparator<Pair>() {

			@Override
			public int compare(Pair o1, Pair o2) {
				return o1.termId - o2.termId;
			}
		});
		return result;
	}

	/**
	 * 2021-2-22 shangchao添加：将报告中每个单词的termId和频率对连接起来
	 * @param ids
	 * @return
	 */
	private String concatenateIds(ArrayList<Pair> ids) {
		StringBuilder builder = new StringBuilder();
		if (ids.size() > 0) {
			builder.append(ids.get(0));
		}
		for (int i = 1; i < ids.size(); i++) {
			builder.append(',').append(ids.get(i));
		}
		return builder.toString();
	}

	/**
	 * 2021-2-22 shangchao添加：这里输出的应该是 reports-large.txt文件
	 * 我需要根据ID来从elasticsearch中查询对应的summary和description的语义向量
	 * @param report
	 * @param out
	 */
	protected void outputReport(DetectionBugReport report, PrintStream out) {
		System.out.println("outputting report " + report.getId());

		out.println("ID=" + report.getId());

		out.println("S-U=" + concatenateIds(this.identify(report.getSummaryTFCollection())));
		out.println("S-B=" + concatenateIds(this.identify(report.getBigramSummaryTFCollection())));
		out.println("S-T=" + concatenateIds(this.identify(report.getTrigramSummaryTFCollection())));

		out.println("D-U=" + concatenateIds(this.identify(report.getDescriptionTFCollection())));
		out.println("D-B=" + concatenateIds(this.identify(report.getBigramDescriptionTFCollection())));
		out.println("D-T=" + concatenateIds(this.identify(report.getTrigramDescriptionTFCollection())));

		out.println("A-U=" + concatenateIds(this.identify(report.getTotalTFCollection())));
		out.println("A-B=" + concatenateIds(this.identify(report.getBigramTotalCollection())));
		out.println("A-T=" + concatenateIds(this.identify(report.getTrigramTotalCollection())));

		out.println("DID=" + (report.isDuplicated() ? report.getDuplicateId() : ""));

		// --
		// version
		// component
		// sub-component
		// issue-type
		// priority
		out.println("VERSION=" + this.versionOrderMap.getVersionOrder(report.getVersion(), report.getId()));
		out.println("COMPONENT=" + this.getIdentifier(report.getComponent()));
		out.println("SUB-COMPONENT=" + this.getIdentifier(report.getSubComponent()));
		String issueType = report.getIssueType();
		// if (issueType.equalsIgnoreCase("PATCH")) {
		// issueType = "DEFECT";
		// } else if (issueType.equalsIgnoreCase("ENHANCEMENT")) {
		// issueType = "FEATURE";
		// }
		out.println("TYPE=" + this.getIdentifier(issueType));
		out.println("PRIORITY=" + report.getPriority());

		// 2021-2-22 shangchao 添加：向reports-large.txt中输出summary和description的语义向量
		String summary_semantic = "";
		String desc_semantic = "";
		try {
			Map<String, Object> stringObjectMap = restClientUtils.searchReportSemanticVector(String.valueOf(report.getId()));
			if (stringObjectMap != null) {
				if (stringObjectMap.containsKey("summary_vector_split_tri")) {
					ArrayList<Double> summary_vector_split_tri = (ArrayList<Double>) stringObjectMap.get("summary_vector_split_tri");
					if (summary_vector_split_tri != null) {
						summary_semantic = summary_vector_split_tri.toString();
					}
				}
				if (stringObjectMap.containsKey("desc_vector_split_tri")) {
					ArrayList<Double> desc_vector_split_tri = (ArrayList<Double>) stringObjectMap.get("desc_vector_split_tri");
					if (desc_vector_split_tri != null) {
						desc_semantic = desc_vector_split_tri.toString();
					}
				}
			}
		} catch (IOException e) {
			System.out.println("elasticsearch搜索出现错误 + message：" + e);
			e.printStackTrace();
		}
		out.println("SUMMARY-SEMANTIC=" + summary_semantic);
		out.println("DESC-SEMANTIC=" + desc_semantic);
	}

	private void identify() {
		BufferedReader in = null;		// in 读入的是 reports.txt文件
		PrintStream out = null;			// out 输出的是 reports-large.txt文件
		try {
			in = new BufferedReader(new FileReader(summaryPath));

			// 我的目标就是这个 Result File，也就是 reports-large.txt文件
			out = openResultStream();

			for (String line = in.readLine(); line != null; line = in.readLine()) {
				// 跳过 reports.txt文件中的每个报告之间的分隔符
				if (line.startsWith(ISummaryFileFormatConstants.REPORT_SEPARATOR)) {
					continue;
				}
				StringWriter stringWriter = new StringWriter();
				PrintWriter writer = new PrintWriter(stringWriter);

				// 遍历每一行，并判断行首的TAG是否是 OS 或者 OD，跳过（忽略）OS和OD
				while (line != null && !line.startsWith(ISummaryFileFormatConstants.REPORT_SEPARATOR)) {
					if (line.startsWith(ISummaryFileFormatConstants.KEY_ORIGINAL_DESCRIPTION)
							|| line.startsWith(ISummaryFileFormatConstants.KEY_ORIGINAL_SUMMARY)) {
					} else {
						writer.println(line);
					}
					line = in.readLine();
				}

				// System.out.println(stringWriter.toString());
				Properties properties = PropertiesReader.readProperties(new StringReader(stringWriter.toString()));

				// 解析 ID
				int id = Integer.parseInt(properties.getProperty(ISummaryFileFormatConstants.KEY_ID));
				// String originalSummary =
				// properties.getProperty(ISummaryFileFormatConstants.KEY_ORIGINAL_SUMMARY);

				// 解析 PS
				String processedSummary = properties.getProperty(ISummaryFileFormatConstants.KEY_PROCESSED_SUMMARY);
				// String originalDescription =
				// properties.getProperty(ISummaryFileFormatConstants.KEY_ORIGINAL_DESCRIPTION);

				// 解析 PD
				String processedDescription = properties.getProperty(ISummaryFileFormatConstants.KEY_PROCESSED_DESCRIPTION);
				// ---

				// 解析 COMP
				String component = properties.getProperty(ISummaryFileFormatConstants.KEY_COMPONENET);

				// 解析 PRIO
				int priority=0 ;
				try
				{
				  priority = Integer.parseInt(properties.getProperty(ISummaryFileFormatConstants.KEY_PRIORITY));
				}catch(Throwable t)
				{
                       
					System.err.println("Error in getting priority,,,");
				}

				// 解析 VER
				String version = properties.getProperty(ISummaryFileFormatConstants.KEY_VERSION);

				// 解析 SUB_COMP
				String subComponent = properties.getProperty(ISummaryFileFormatConstants.KEY_SUB_COMPONENT);

				// 解析 ISSUE_TYPE
				String issueType = properties.getProperty(ISummaryFileFormatConstants.KEY_ISSUE_TYPE);

				// 解析 DID
				int dupID=DetectionBugReport.NON_DUPLICATE;
				String temp = properties.getProperty(ISummaryFileFormatConstants.KEY_DUPLICATE_ID);
				if (temp.length() == 0) {
					dupID = DetectionBugReport.NON_DUPLICATE;
				} else {
					if(temp!=null)
					{
					dupID = Integer.parseInt(temp);
					}
				}
				if (processedDescription == null || processedSummary == null) {
					throw new RuntimeException("this is impossible. any field should not be null.");
				}
				DetectionBugReport report;
				if (DetectionBugReport.isDuplicate(id, dupID)) {
					report = new DuplicateDetectionBugReport(id, " ", processedSummary, " ", processedDescription, dupID,
							priority, version, subComponent, issueType, component);

				} else {
					report = new MasterDetectionBugReport(id, " ", processedSummary, " ", processedDescription, dupID, priority,
							version, subComponent, issueType, component);
				}
				this.outputReport(report, out);
			}
		} catch (IOException e) {
			throw new RuntimeException("io exception occured.", e);
		} finally {
			if (in != null) {
				try {
					in.close();
				} catch (IOException e) {
				}
			}
			if (out != null) {
				out.close();
			}
		}
	}

	private PrintStream openResultStream() throws FileNotFoundException {
		if (!this.resultPath.getParentFile().exists()) {
			this.resultPath.getParentFile().mkdirs();
		}
		PrintStream printStream = new PrintStream(this.resultPath);
		return printStream;
	}

}
